////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 1999 by Thierry Maurel
// All rights reserved
//
// Distribute freely, except: don't remove my name from the source or
// documentation (don't take credit for my work), mark your changes (don't
// get me blamed for your possible bugs), don't alter or remove this
// notice.
// No warrantee of any kind, express or implied, is included with this
// software; use at your own risk, responsibility for damages (if any) to
// anyone resulting from the use of this software rests entirely with the
// user.
//
// Send bug reports, bug fixes, enhancements, requests, flames, etc., and
// I'll try to keep a version up to date.  I can be reached as follows:
//    tmaurel@caramail.com
//
////////////////////////////////////////////////////////////////////////////////
// Version : 1.0					   * Author : T.Maurel
// Date    : 30.08.99
//
// MultiRectTracker.cpp: implementation of the CMultiRectTracker class.
//
//////////////////////////////////////////////////////////////////////
//
// This class is useful for vector editors, or similar applications
// where the user can select or move some objects on the screen.
// Anywhere you use CRectTracker, you can use this extended version,
// with the same options for drawing (handle size, style...).
// The principle is contains in a loop : for each object, update the 
// m_rect and call the CRectTracker corresponding function.
// The m_rect is not used in other cases, so there is no potential pb.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "MultiRectTracker.h"
#include "MRTOprt.h"
#include "LineTracker.h"
#include "Utility.h"
#include <direct.h>
#include "resource.h"

#include "MyMEMDC.h"

#define CX_BORDER 2
#define CY_BORDER 2

#ifdef _VIWCONTROL
#include "ViwControl.h"
#include "DlgChartType.h"
#endif
#include "XMLSettings.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#define new DEBUG_NEW
#endif

AFX_STATIC_DATA HBRUSH _afxHatchBrush = 0;
AFX_STATIC_DATA HPEN _afxBlackDottedPen = 0;
AFX_STATIC_DATA HPEN _afxBlackSolidPen = 0;

#define isCtrlPressed	((0x8000 & GetKeyState(VK_CONTROL)) != 0)

// extern void AFXAPI AfxDeleteObject(HGDIOBJ* pObject);
extern void AFXAPI AfxUnlockGlobals(int nLockType);
#define CRIT_RECTTRACKER	5

extern void AFX_CDECL AfxTrackerTerm();
extern char _afxTrackerTerm;

AFX_STATIC_DATA HCURSOR _afxCursors[10] =
{
	0, 
};

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
//
//
CMultiRectTracker::CMultiRectTracker()
{
	m_posCurr = NULL;
	m_bAllowInvert = TRUE;
	m_nHandleSize = 5;
	m_nStyle = solidLine | resizeOutside;
	m_dwAdd = 0;
	m_strAdd = _T("CChartWHoriObj");
	m_pEdit = NULL;
	m_pObject = NULL;
	m_pOldObj = NULL;
	m_clrRect = RGB(10, 100, 130);
	m_pWnd = NULL;

	m_pDoc = NULL;
	CreatePen();

	m_nRCF = ::RegisterClipboardFormat(_T("MRT Object"));

	m_nXMLSF = ::RegisterClipboardFormat(_T("XML Spreadsheet"));
	m_nHTMLF = ::RegisterClipboardFormat(_T("HTML Format"));
	m_nRTFF = ::RegisterClipboardFormat(CF_RTF);
	m_nCSVF = ::RegisterClipboardFormat(_T("Csv"));

	static BOOL bInitialized;
	if (!bInitialized)
	{
		// Note: all track cursors must live in same module
		HINSTANCE hInst = AfxFindResourceHandle(MAKEINTRESOURCE(AFX_IDC_TRACK4WAY),
							RT_GROUP_CURSOR);

		// initialize the cursor array
		_afxCursors[0] = ::LoadCursor(hInst,
							MAKEINTRESOURCE(AFX_IDC_TRACKNWSE));
		_afxCursors[1] = ::LoadCursor(hInst,
							MAKEINTRESOURCE(AFX_IDC_TRACKNESW));
		_afxCursors[2] = _afxCursors[0];
		_afxCursors[3] = _afxCursors[1];
		_afxCursors[4] = ::LoadCursor(hInst, MAKEINTRESOURCE(AFX_IDC_TRACKNS));
		_afxCursors[5] = ::LoadCursor(hInst, MAKEINTRESOURCE(AFX_IDC_TRACKWE));
		_afxCursors[6] = _afxCursors[4];
		_afxCursors[7] = _afxCursors[5];
		_afxCursors[8] = ::LoadCursor(hInst,
							MAKEINTRESOURCE(AFX_IDC_TRACK4WAY));
		_afxCursors[9] = ::LoadCursor(NULL, IDC_CROSS);
	}
}

//////////////////////////////////////////////////////////////////////
//
//
CMultiRectTracker::~CMultiRectTracker()
{
	DestroyEdit();
	ClearObjects();
	m_Objects.RemoveAll();
	ClearPositions();
}

void CMultiRectTracker::CreatePen()
{
	//if (_afxBlackDottedPen == NULL)
	{
		// create black dotted pen
		_afxBlackDottedPen = ::CreatePen(PS_DOT, 0, m_clrRect);
		if (_afxBlackDottedPen == NULL)
		{
			AfxUnlockGlobals(CRIT_RECTTRACKER);
			AfxThrowResourceException();
		}
	}
	//if (_afxBlackSolidPen == NULL)
	{
		// create black dotted pen
		_afxBlackSolidPen = ::CreatePen(PS_SOLID, 0, m_clrRect);
		if (_afxBlackSolidPen == NULL)
		{
			AfxUnlockGlobals(CRIT_RECTTRACKER);
			AfxThrowResourceException();
		}
	}
}

void CMultiRectTracker::SetRectColor(COLORREF clrRect)
{
	m_clrRect = clrRect;
	CreatePen();
}
//////////////////////////////////////////////////////////////////////
// API
//////////////////////////////////////////////////////////////////////
// Object pointers management, very simple.
//////////////////////////////////////////////////////////////////////
//
//
void CMultiRectTracker::Add(CMRTObject* pObject)
{
	m_Objects.Add(pObject);
	m_pObject = pObject;
	m_pObject->SetDoc(m_pDoc);
}

void CMultiRectTracker::Remove(CMRTObject* pObject)
{
	for (int i = m_Objects.GetSize() - 1; i > -1; i--)
	{
		if (m_Objects[i] == pObject)
		{
			m_Objects.RemoveAt(i);
			break;
		}
	}

	if (m_pObject == pObject)
	{
		if (m_Objects.GetSize())
			m_pObject = m_Objects.GetAt(m_Objects.GetSize() - 1);
		else
			m_pObject = NULL;
	}
}

int CMultiRectTracker::Find(CMRTObject* pObject)
{
	int i = m_Objects.GetSize() - 1;
	for (; i > -1; i--)
	{
		if (m_Objects[i] == pObject)
			break;
	}
	return i;
}

//////////////////////////////////////////////////////////////////////
//
//
void CMultiRectTracker::RemoveAll()
{
	m_Objects.RemoveAll();
	m_pObject = NULL;
	m_rect.SetRectEmpty();
}

void CMultiRectTracker::ClearObjects()
{
	while (m_lstObjs.GetCount())
		delete m_lstObjs.RemoveHead();

	while (m_lstAdds.GetCount())
		delete m_lstAdds.RemoveHead();

	while (m_lstDels.GetCount())
		delete m_lstDels.RemoveHead();
	
	while (m_lstGrps.GetCount())
		delete m_lstGrps.RemoveHead();

	while (m_lstStack.GetCount())
		delete m_lstStack.RemoveHead();
	m_posCurr = NULL;
}

void CMultiRectTracker::Reorder(long lOrder)
{
	AddOprt(new CMRTReorder(&m_Objects, &m_lstObjs, lOrder));
}

void CMultiRectTracker::Group()
{
	CMRTOprt* pGroup = new CMRTGroup(&m_Objects, &m_lstObjs, &m_lstGrps);
 	AddOprt(pGroup);
	Select(pGroup);
}

void CMultiRectTracker::Ungroup()
{
	CMRTOprt* pUngroup = new CMRTUngroup(&m_Objects, &m_lstObjs, &m_lstGrps);
	AddOprt(pUngroup);
	Select(pUngroup);
}

void CMultiRectTracker::AddOprt(CMRTOprt* pOprt)
{
	while (m_posCurr != m_lstStack.GetTailPosition())
		delete m_lstStack.RemoveTail();

	if (pOprt->Do())
		m_posCurr = m_lstStack.AddTail(pOprt);
	else
		delete pOprt;
}

//////////////////////////////////////////////////////////////////////
// Do the whole work, decide if the user moves objects or extends only one.
//
BOOL CMultiRectTracker::Track(CWnd* pWnd, CPoint point, CDC* pDC,
	BOOL bAllowInvert /* = FALSE */, CWnd* pWndClipTo /* = NULL */)
{
	// If the array is empty, do nothing...
	if (IsEmpty())
		return FALSE;

	// perform hit testing on the handles
	int nHandle = HitTestHandles(point, pDC);
	if (nHandle == hitNothing)
	{
		// didn't hit a handle, so just return FALSE
		return FALSE;
	}

	if (nHandle == hitMiddle)
	{
		// The selected objects will be moved on the screen
		return MultiTrackHandle(pWnd, point, pDC, pWndClipTo);
	}
	else
	{
		// Update the only one object selected,
		// and call CRectTracker::TrackHandle
		m_Objects.RemoveAll();
		m_Objects.Add(m_pObject);
		if (m_pObject->IsLocked())
			return FALSE;

		m_rect.CopyRect(m_pObject->GetRect());
		pDC->LPtoDP(m_rect);

		// otherwise, call helper function to do the tracking
		m_bAllowInvert = bAllowInvert;
		BOOL bRet = FALSE;
		if ((m_pObject->m_dwProp == 0x0010) || (m_pObject->m_dwProp == 0x0050))
		{
			if (nHandle > hitMiddle)
			{
				CPolylineTracker tracker;
				tracker.m_rect = m_rect;
				tracker.m_points.RemoveAll();

				CPolylineObject* pPlObj = (CPolylineObject*) m_pObject;
				int nIndex = nHandle - 9;
				int nLast = pPlObj->m_points.GetSize() - 1;
				int nAdded = -1;
				if (nIndex > 0)
					tracker.m_points.Add(pPlObj->m_points[nIndex - 1]);
				else if (pPlObj->GetObjType() == _T("PolygonObj"))
				{
					tracker.m_points.Add(pPlObj->m_points[nLast]);
					nAdded = nLast;
				}

				tracker.m_points.Add(pPlObj->m_points[nIndex]);
				if (nIndex < nLast)
					tracker.m_points.Add(pPlObj->m_points[nIndex + 1]);
				else if (pPlObj->GetObjType() == _T("PolygonObj"))
				{
					tracker.m_points.Add(pPlObj->m_points[0]);
					nAdded = 0;
				}

				tracker.m_bThree = (tracker.m_points.GetSize() == 3);

				int i = 0;
				int nSize = pPlObj->m_points.GetSize();
				for (; i < nSize; i++)
				{
					if (abs(i - nIndex) < 2)
						continue;

					if (pPlObj->GetObjType() == _T("PolygonObj"))
					{
						if (i == nAdded)
							continue;
					}

					tracker.m_points.Add(pPlObj->m_points[i]);
				}

				CRect rcPl = pPlObj->GetRect();
				for (i = 0; i < tracker.m_points.GetSize(); i++)
				{
					tracker.m_points[i].x += rcPl.left;
					tracker.m_points[i].y += rcPl.top;
					pDC->LPtoDP(&tracker.m_points[i]);
				}

				if (tracker.TrackHandle(nHandle, pWnd, point, pWndClipTo))
				{
					CMRTProp* pProp = new CMRTProp(pPlObj, 0x0001);
					m_rect = tracker.m_rect;
					CRect rect = rcPl;
					pPlObj->m_points[nIndex] = tracker.m_points.GetAt(nIndex ?
																	1 :
																	(tracker.m_bThree ?
						1 :
						0));
					pDC->DPtoLP(&pPlObj->m_points[nIndex]);
					rcPl = tracker.m_rect;
					pDC->DPtoLP(&rcPl);
					pPlObj->m_rectPosition = rcPl;
					CPoint ptOffset;
					ptOffset.x = rect.left - rcPl.left;
					ptOffset.y = rect.top - rcPl.top;
					for (i = 0; i < pPlObj->m_points.GetSize(); i++)
					{
						if (i != nIndex)
						{
							pPlObj->m_points[i].x += ptOffset.x;
							pPlObj->m_points[i].y += ptOffset.y;
						}
						else
						{
							pPlObj->m_points[i].x -= rcPl.left;
							pPlObj->m_points[i].y -= rcPl.top;
						}
					}
					AddOprt(pProp);
					return TRUE;
				}
			}
			else
			{
				CLineTracker tracker;
				tracker.m_rect = m_rect;
				if (tracker.TrackHandle(nHandle, pWnd, point, pWndClipTo))
				{
					m_rect = tracker.m_rect;
					pDC->DPtoLP(&tracker.m_rect);
					AddOprt(new CMRTMove(m_pObject, tracker.m_rect));
					return TRUE;
				}
			}
		}
		else if (TrackHandleEx(nHandle, pWnd, point, pWndClipTo, pDC))
		{
			CRect rcObject = m_rect;
			pDC->DPtoLP(&rcObject);
			AddOprt(new CMRTMove(m_pObject, rcObject));
			//m_pObject->SetRect(rcObject);
			return TRUE;
		}
		return FALSE;
	}
}

//////////////////////////////////////////////////////////////////////
// Very classic drawing function
//
void CMultiRectTracker::Draw(CDC* pDC)
{
	for (int index = 0; index < m_Objects.GetSize(); index++)
	{
		CMRTObject* pObject = m_Objects.GetAt(index);
		m_rect.CopyRect(pObject->GetRect());
		pDC->LPtoDP(&m_rect);
		OwnerDraw(pDC, pObject, (pObject == m_pObject));
	}
}

struct AFX_HANDLEINFO
{
	size_t nOffsetX;	// offset within RECT for X coordinate
	size_t nOffsetY;	// offset within RECT for Y coordinate
	int nCenterX;   	// adjust X by Width()/2 * this number
	int nCenterY;   	// adjust Y by Height()/2 * this number
	int nHandleX;   	// adjust X by handle size * this number
	int nHandleY;   	// adjust Y by handle size * this number
	int nInvertX;   	// handle converts to this when X inverted
	int nInvertY;   	// handle converts to this when Y inverted
};

// this array describes all 8 handles (clock-wise)
AFX_STATIC_DATA const AFX_HANDLEINFO _afxHandleInfo[] =
{
	// corner handles (top-left, top-right, bottom-right, bottom-left
	{ offsetof(RECT, left), offsetof(RECT, top),		0, 0,  0,  0, 1, 3 },
	{ offsetof(RECT, right), offsetof(RECT, top),   	0, 0, -1,  0, 0, 2 },
	{ offsetof(RECT, right), offsetof(RECT, bottom),	0, 0, -1, -1, 3, 1 },
	{ offsetof(RECT, left), offsetof(RECT, bottom), 	0, 0,  0, -1, 2, 0 },

	// side handles (top, right, bottom, left)
	{ offsetof(RECT, left), offsetof(RECT, top),		1, 0,  0,  0, 4, 6 },
	{ offsetof(RECT, right), offsetof(RECT, top),   	0, 1, -1,  0, 7, 5 },
	{ offsetof(RECT, left), offsetof(RECT, bottom), 	1, 0,  0, -1, 6, 4 },
	{ offsetof(RECT, left), offsetof(RECT, top),		0, 1,  0,  0, 5, 7 }
};

void CMultiRectTracker::GetDrawHandleRect(int nHandle, CRect* pHandleRect) const
{
	ASSERT(nHandle < 8);

	// get normalized rectangle of the tracker
	CRect rectT = m_rect;
	rectT.NormalizeRect();
	if ((m_nStyle & (solidLine | dottedLine)) != 0)
		rectT.InflateRect(+1, +1);

	// since the rectangle itself was normalized, we also have to invert the
	//  resize handles.
	nHandle = NormalizeHit(nHandle);

	// handle case of resize handles outside the tracker
	int size = GetHandleSize();
	if (m_nStyle & resizeOutside)
		rectT.InflateRect((size - 1) / 2, (size - 1) / 2);

	// calculate position of the resize handle
	int nWidth = rectT.Width();
	int nHeight = rectT.Height();
	CRect rect;
	const AFX_HANDLEINFO* pHandleInfo = &_afxHandleInfo[nHandle];
	rect.left = *(int *) ((BYTE *) &rectT + pHandleInfo->nOffsetX);
	rect.top = *(int *) ((BYTE *) &rectT + pHandleInfo->nOffsetY);
	rect.left += size * pHandleInfo->nHandleX;
	rect.top += size * pHandleInfo->nHandleY;
	rect.left += pHandleInfo->nCenterX * (nWidth - size) / 2;
	rect.top += pHandleInfo->nCenterY * (nHeight - size) / 2;
	rect.right = rect.left + size;
	rect.bottom = rect.top + size;

	*pHandleRect = rect;
}

void CMultiRectTracker::OwnerDraw(CDC* pDC, CMRTObject* pObject,
	BOOL bHighLight /* = TRUE */)
{
	CRect rectPositong = pObject->GetRect();
	pDC->LPtoDP(&rectPositong);

	// set initial DC state
	VERIFY(pDC->SaveDC() != 0);
	pDC->SetMapMode(MM_TEXT);
	pDC->SetViewportOrg(0, 0);
	pDC->SetWindowOrg(0, 0);

	// get normalized rectangle
	CRect rect = m_rect;

	CPen* pOldPen = NULL;
	CBrush* pOldBrush = NULL;
	CGdiObject* pTemp;
	int nOldROP;

	// draw lines
	if ((m_nStyle & (dottedLine | solidLine)) != 0)
	{
		if (m_nStyle & dottedLine)
			pOldPen = pDC->SelectObject(CPen::FromHandle(_afxBlackDottedPen));
		else
			pOldPen = pDC->SelectObject(CPen::FromHandle(_afxBlackSolidPen));
		pOldBrush = (CBrush *) pDC->SelectStockObject(NULL_BRUSH);
		nOldROP = pDC->SetROP2(R2_COPYPEN);
		pObject->DrawTracker(pDC, rect);
		pDC->SetROP2(nOldROP);
	}

	// if hatchBrush is going to be used, need to unrealize it
	if ((m_nStyle & (hatchInside | hatchedBorder)) != 0)
		UnrealizeObject(_afxHatchBrush);

	// hatch inside
	if ((m_nStyle & hatchInside) != 0)
	{
		pTemp = pDC->SelectStockObject(NULL_PEN);
		if (pOldPen == NULL)
			pOldPen = (CPen *) pTemp;
		pTemp = pDC->SelectObject(CBrush::FromHandle(_afxHatchBrush));
		if (pOldBrush == NULL)
			pOldBrush = (CBrush *) pTemp;
		pDC->SetBkMode(TRANSPARENT);
		nOldROP = pDC->SetROP2(R2_MASKNOTPEN);
		pDC->Rectangle(rect.left + 1, rect.top + 1, rect.right, rect.bottom);
		pDC->SetROP2(nOldROP);
	}

	// draw hatched border
	if ((m_nStyle & hatchedBorder) != 0)
	{
		pTemp = pDC->SelectObject(CBrush::FromHandle(_afxHatchBrush));
		if (pOldBrush == NULL)
			pOldBrush = (CBrush *) pTemp;
		pDC->SetBkMode(OPAQUE);
		CRect rectTrue;
		GetTrueRect(&rectTrue);
		pDC->PatBlt(rectTrue.left, rectTrue.top, rectTrue.Width(),
				rect.top - rectTrue.top, 0x000F0001 /* Pn */);
		pDC->PatBlt(rectTrue.left, rect.bottom, rectTrue.Width(),
				rectTrue.bottom - rect.bottom, 0x000F0001 /* Pn */);
		pDC->PatBlt(rectTrue.left, rect.top, rect.left - rectTrue.left,
				rect.Height(), 0x000F0001 /* Pn */);
		pDC->PatBlt(rect.right, rect.top, rectTrue.right - rect.right,
				rect.Height(), 0x000F0001 /* Pn */);
	}

	// draw resize handles
	if ((m_nStyle & (resizeInside | resizeOutside)) != 0)
	{
		if (bHighLight)
			pObject->DrawHandle(pDC, rectPositong);
	}

	// cleanup pDC state
	if (pOldPen != NULL)
		pDC->SelectObject(pOldPen);
	if (pOldBrush != NULL)
		pDC->SelectObject(pOldBrush);
	VERIFY(pDC->RestoreDC(-1));
}

//////////////////////////////////////////////////////////////////////
// Hit functions
//////////////////////////////////////////////////////////////////////
//
//
int CMultiRectTracker::HitTestHandles(CPoint point, CDC* pDC)
{
	int iHit = hitNothing;
	CLineTracker ltracker;
	for (int index = 0; index < m_Objects.GetSize(); index++)
	{
		CMRTObject* pObj = m_Objects.GetAt(index);
		m_rect.CopyRect(pObj->GetRect());
		pDC->LPtoDP(&m_rect);
		iHit = pObj->HitTest(point, pDC, this);
		if (iHit != hitNothing)
		{
			m_pObject = m_Objects.GetAt(index);
			return iHit;
		}
	}
	return hitNothing;
}

//////////////////////////////////////////////////////////////////////
//
//
int CMultiRectTracker::HitTest(CPoint point, CDC* pDC)
{
	m_pObject = NULL;
	int iHit = hitNothing;
	for (int index = 0; index < m_Objects.GetSize(); index++)
	{
		CMRTObject* pObj = m_Objects.GetAt(index);
		iHit = pObj->HitTest(point, pDC, this);
		if (iHit != hitNothing)
		{
			m_pObject = pObj;
			return iHit;
		}
	}
	return hitNothing;
}

BOOL CMultiRectTracker::DestroyEdit()
{
	if (m_pEdit->GetSafeHwnd())
	{
		if (m_pObject != NULL)
		{
			CMRTProp* pProp = new CMRTProp(m_pObject, 0x0002);
			m_pEdit->GetWindowText(m_pObject->m_strText);
			AddOprt(pProp);
		}
		
		m_pEdit->DestroyWindow();
		delete m_pEdit;
		m_pEdit = NULL;
		return TRUE;
	}
	else if (m_pEdit != NULL)
	{
		delete m_pEdit;
		m_pEdit = NULL;
		return TRUE;
	}

	return FALSE;
}

int CMultiRectTracker::OnLButtonDown(UINT nFlags, CPoint point, CWnd* pWnd, CDC* pDC)
{
	CList<CMRTObject*,CMRTObject*>* pObjects = &m_lstObjs;
	int iResult = 0;
	DestroyEdit();

	m_pOldObj = m_pObject;

	// Ask the multitrack if an object is already selected
	// or a handle. If not, start the local tracker.
	if (HitTest(point, pDC) < 0)
	{
		m_pOldObj = NULL;
		if (m_dwAdd)
		{
			RemoveAll();

			CMRTObject* pObject = NULL;
			switch (m_dwAdd)
			{
			case 1:
				// Line
				{
					CLineTracker tracker;
					if (tracker.TrackRubberBand(pWnd, point))
					{
						pObject = new CLineObject;
						m_rect = tracker.m_rect;
						pDC->DPtoLP(&m_rect);
						pObject->SetRect(m_rect);
						//pObjects->AddTail(pObject);
						point = tracker.m_rect.TopLeft();
					}
				}
				break;
			case 4:
				// Polyline
				{
					CPolylineTracker tracker;
					if (tracker.TrackRubberBand(pWnd, point))
					{
						CPolylineObject* pPlObj = new CPolylineObject;
						pObject = pPlObj;
						m_rect = tracker.m_rect;
						pDC->DPtoLP(&m_rect);
						pObject->SetRect(m_rect);
						pPlObj->m_points.Copy(tracker.m_points);
						for (int i = 0; i < pPlObj->m_points.GetSize(); i++)
						{
							pDC->DPtoLP(&pPlObj->m_points[i]);
							pPlObj->m_points[i].x -= m_rect.left;
							pPlObj->m_points[i].y -= m_rect.top;
						}
						//pObjects->AddTail(pObject);
						point = tracker.m_rect.TopLeft();
					}
				}
				break;
			case 5:
				// Polygon
				{
					CPolylineTracker tracker;
					if (tracker.TrackRubberBand(pWnd, point))
					{
						CPolylineObject* pPlObj = new CPolygonObject;
						pObject = pPlObj;
						m_rect = tracker.m_rect;
						pDC->DPtoLP(m_rect);
						pObject->SetRect(m_rect);
						pPlObj->m_points.Copy(tracker.m_points);
						for (int i = 0; i < pPlObj->m_points.GetSize(); i++)
						{
							pDC->DPtoLP(&pPlObj->m_points[i]);
							pPlObj->m_points[i].x -= m_rect.left;
							pPlObj->m_points[i].y -= m_rect.top;
						}
						//pObjects->AddTail(pObject);
						point = tracker.m_rect.TopLeft();
					}
				}
				break;
			case 3:
				// Ellipse
				{
					CEllipseTracker tracker;
					if (tracker.TrackRubberBand(pWnd, point))
					{
						tracker.m_rect.NormalizeRect();
						pObject = new CEllipseObject;
						m_rect = tracker.m_rect;
						pDC->DPtoLP(m_rect);
						pObject->SetRect(m_rect);
						//pObjects->AddTail(pObject);
					}
				}
				break;
            case 6:
                // Picture
                {
                    CRectTracker tracker; 
                    if (tracker.TrackRubberBand(pWnd, point))
                    {
                        tracker.m_rect.NormalizeRect();
                        pObject = new CPictureObject(); 
                        m_rect = tracker.m_rect; 
                        pDC->DPtoLP(m_rect); 
                        pObject->SetRect(m_rect); 
                    }
                }
                break; 
			case 2:
			default:
                // Rectangle
				{
					CRectTracker tracker;
					if (tracker.TrackRubberBand(pWnd, point))
					{
						tracker.m_rect.NormalizeRect();
						CRuntimeClass* pRC = NULL;

#ifdef _VIWCONTROL
						if (m_dwAdd != 2)
						{
							CDlgChartType dlg;
							dlg.m_strChart = m_strAdd;
							if (dlg.DoModal() == IDOK)
							{
								m_strAdd = dlg.m_strChart;
							}

							if (m_strAdd.GetLength())
								pObject = CMRTObject::CreateObj(m_strAdd);
						}
#endif						
						if (NULL == pObject)
							pObject = new CRectObject;

						m_rect = tracker.m_rect;
						pDC->DPtoLP(m_rect);
						pObject->SetRect(m_rect);
						// pObjects->AddTail(pObject);
					}
				}
				break;
			}

			if (pObject != NULL)
			{
				AddOprt(new CMRTAdd(pObject, pObjects, &m_lstAdds));
				//pObjects->AddTail(pObject);
				Add(pObject);
				m_dwAdd = 0;
				iResult = 1;
			}
		}
		else
		{
			// Reset the multitrack only if there
			// is no CTRL key.
			if (!(nFlags & MK_CONTROL))
				RemoveAll();

			CRect rcObject;
			CMRTObject* pObject;
			
			// No rubber band, see if the point selects an object.
			POSITION pos = pObjects->GetHeadPosition();
			while (pos != NULL)
			{
				pObject = pObjects->GetNext(pos);
				if (pObject->HitTest(point, pDC, this) != hitNothing)
				{
					Add(pObject);
					iResult = 1;
					break;
				}
			}
			
			if (iResult != 1)
			{
				// local tracker...
				CRectTracker tracker;
				if (tracker.TrackRubberBand(pWnd, point, TRUE))
				{
					// see if rubber band intersects with the objects
					CRect rectT;
					tracker.m_rect.NormalizeRect();
					POSITION pos = pObjects->GetHeadPosition();
					while (pos != NULL)
					{
						pObject = pObjects->GetNext(pos);
						rcObject = pObject->GetRect();
						pDC->LPtoDP(&rcObject);
						rcObject.NormalizeRect();
						if (rcObject.IsRectEmpty())
						{
							if (tracker.m_rect.PtInRect(rcObject.TopLeft()) &&
								tracker.m_rect.PtInRect(rcObject.BottomRight()))
							{
								Add(pObject); // add it to the multitrack,
								// and continue the loop
							}
						}
						else
						{
							rectT.IntersectRect(tracker.m_rect, rcObject);
							if (rectT == rcObject)
							{
								Add(pObject); // add it to the multitrack,
								// and continue the loop
							}
						}
					}
					iResult = m_Objects.GetSize() ? 1 : 0;
				}
			}
		}
	}
	else
	{
		// Click on the multitrack, forward actions to it.
		if (Track(pWnd, point, pDC, FALSE))
		{
			iResult = 1;
		}
		else if (m_pObject == m_pOldObj)
		{
			if (!(nFlags & MK_CONTROL)) // select next if there exists
			{
				RemoveAll();

				// No rubber band, see if the point selects an object.
				POSITION pos = pObjects->Find(m_pOldObj);
				POSITION pos0 = pos;
				pObjects->GetNext(pos);

				for (; pos != NULL;)
				{
					CMRTObject* pObject = pObjects->GetNext(pos);
					if (pObject->HitTest(point, pDC, this) != hitNothing)
					{
						Add(pObject);
						iResult = 1;
						break;
					}
				}

				if (iResult != 1)
				{
					pos = pObjects->GetHeadPosition();
					for (; pos != pos0;)
					{
						CMRTObject* pObject = pObjects->GetNext(pos);
						if (pObject->HitTest(point, pDC, this) != hitNothing)
						{
							Add(pObject);
							iResult = 1;
							break;
						}
					}
				}

				if (iResult != 1)
				{
					Add(m_pOldObj);
					iResult = 1;
				}
			}
		}
	}
	// Update drawing.
	pWnd->Invalidate();
	pWnd->UpdateWindow();
	return iResult;
}

int CMultiRectTracker::OnLButtonDblClk(CPoint point, CWnd* pWnd, CDC* pDC)
{
	if (::GetCapture() != NULL)
		return 1;

	CRect rcObject;
	if (m_pObject != NULL)
	{
		if ((m_pOldObj != NULL) && (m_pOldObj != m_pObject))
		{
			RemoveAll();
			Add(m_pOldObj);
			pWnd->Invalidate();
		}
		
		rcObject = m_pObject->GetRect();
		pDC->LPtoDP(&rcObject);
		rcObject.NormalizeRect();
		if (CreateInPlaceEdit(rcObject, pWnd))
			return 1;

		OnEditProp(pWnd);
		return 1;
	}
	return 0;
}

int CMultiRectTracker::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags,
	CWnd* pWnd, CDC* pDC)
{
	if (m_pObject != NULL)
	{
		CRect rcObject = m_pObject->GetRect();
		pDC->LPtoDP(&rcObject);
		rcObject.NormalizeRect();
		if (CreateInPlaceEdit(rcObject, pWnd))
			m_pEdit->SendMessage(WM_CHAR, nChar, nRepCnt);
	}

	return 0;
}

BOOL CMultiRectTracker::CreateInPlaceEdit(CRect rectEdit, CWnd* pWnd)
{
	if (m_pEdit->GetSafeHwnd())
	{
		// m_pEdit->DestroyWindow();
		return TRUE;
	}

	if (m_pObject->IsReadonly())
	{
		// m_pEdit = NULL;
		return FALSE;
	}

	m_pEdit = m_pObject->CreateInPlaceEdit(rectEdit, pWnd);
	if (m_pEdit != NULL)
		return TRUE;
	return FALSE;
}

//////////////////////////////////////////////////////////////////////
// Ask each objects to get a cursor change or not
//
BOOL CMultiRectTracker::SetCursor(CWnd* pWnd, UINT nHitTest, CDC* pDC)
{
	// trackers should only be in client area
	if (nHitTest != HTCLIENT)
		return FALSE;

	// convert cursor position to client co-ordinates
	CPoint point;
	GetCursorPos(&point);
	pWnd->ScreenToClient(&point);

	CRect rectSave = m_rect;
	for (int index = 0; index < m_Objects.GetSize(); index++)
	{
		CMRTObject* pObj = m_Objects.GetAt(index);
		if (pObj->IsLocked())
			continue;

		int nHandle = pObj->HitTest(point, pDC, this);
		// do hittest and normalize hit
		if (nHandle < 0)
			continue;
		if (nHandle > 8)
			nHandle = 9;
		::SetCursor(_afxCursors[nHandle]);
		return TRUE;
	}
	m_rect = rectSave;
	return FALSE;
}


void CMultiRectTracker::EraseTrackerRect(LPCRECT lpRect, CWnd* pWndClipTo,
	CDC* pDC, CWnd* pWnd)
{
	// first, normalize the rectangle for drawing
	CRect rect = *lpRect;
	rect.NormalizeRect();

	// convert to client coordinates
	if (pWndClipTo != NULL)
	{
		pWnd->ClientToScreen(&rect);
		pWndClipTo->ScreenToClient(&rect);
	}

	CSize size(0, 0);
	if (!m_bFinalErase)
	{
		// otherwise, size depends on the style
		if (m_nStyle & hatchedBorder)
		{
			size.cx = size.cy = max(1, GetHandleSize(rect) - 1);
			rect.InflateRect(size);
		}
		else
		{
			size.cx = CX_BORDER;
			size.cy = CY_BORDER;
		}
	}

	// remember last rectangles
	m_rectLast = rect;
	m_sizeLast = size;

	rect.SetRectEmpty();

	// and erase it
	if (m_rectLast.IsRectEmpty())
	{
		if (m_bFirstErase)
		{
			m_bFirstErase = FALSE;
		}
		else
		{
			CPen pen(PS_DOT, 1, RGB(255, 255, 255));
			CPen* pOldPen = pDC->SelectObject(&pen);
			int nROP2 = pDC->SetROP2(R2_XORPEN);
			pDC->MoveTo(m_rectLast.TopLeft());
			pDC->LineTo(m_rectLast.BottomRight());
			pDC->SetROP2(nROP2);
			pDC->SelectObject(pOldPen);
		}
	}
	else
		pDC->DrawDragRect(rect, size, m_rectLast, m_sizeLast);
}

void CMultiRectTracker::DrawTrackerRect(LPCRECT lpRect, CWnd* pWndClipTo,
	CDC* pDC, CWnd* pWnd)
{
	// first, normalize the rectangle for drawing
	CRect rect = *lpRect;
	rect.NormalizeRect();

	// convert to client coordinates
	if (pWndClipTo != NULL)
	{
		pWnd->ClientToScreen(&rect);
		pWndClipTo->ScreenToClient(&rect);
	}

	CSize size(0, 0);
	if (!m_bFinalErase)
	{
		// otherwise, size depends on the style
		if (m_nStyle & hatchedBorder)
		{
			size.cx = size.cy = max(1, GetHandleSize(rect) - 1);
			rect.InflateRect(size);
		}
		else
		{
			size.cx = CX_BORDER;
			size.cy = CY_BORDER;
		}
	}

	// and draw it
	if (m_bFinalErase || !m_bErase)
	{
		if (rect.IsRectEmpty())
		{
			CPen pen(PS_DOT, 1, RGB(255, 255, 255));
			CPen* pOldPen = pDC->SelectObject(&pen);
			int nROP2 = pDC->SetROP2(R2_XORPEN);
			pDC->MoveTo(rect.TopLeft());
			pDC->LineTo(rect.BottomRight());
			pDC->SetROP2(nROP2);
			pDC->SelectObject(pOldPen);
		}
		else
			pDC->DrawDragRect(rect, size, m_rectLast, m_sizeLast);
	}

	// remember last rectangles
	m_rectLast = rect;
	m_sizeLast = size;
}

//////////////////////////////////////////////////////////////////////
// Internal functions
//////////////////////////////////////////////////////////////////////
// Inspirated by CRectTarcker::TrackHandle
//
BOOL CMultiRectTracker::MultiTrackHandle(CWnd* pWnd, CPoint point, CDC* pDC,
	CWnd* pWndClipTo)
{
	// don't handle if capture already set
	if (::GetCapture() != NULL)
		return FALSE;

	AfxLockTempMaps();  // protect maps while looping

	ASSERT(!m_bFinalErase);

	// set capture to the window which received this message
	pWnd->SetCapture();
	ASSERT(pWnd == CWnd::GetCapture());
	pWnd->UpdateWindow();
	if (pWndClipTo != NULL)
		pWndClipTo->UpdateWindow();

	CPoint oldPoint = point;


	// We work on the rects copies, not the objects
	CopyPositions(pDC);

	// get DC for drawing
	CDC* pDrawDC;
	if (pWndClipTo != NULL)
	{
		// clip to arbitrary window by using adjusted Window DC
		pDrawDC = pWndClipTo->GetDCEx(NULL, DCX_CACHE);
	}
	else
	{
		// otherwise, just use normal DC
		pDrawDC = pWnd->GetDC();
	}
	ASSERT_VALID(pDrawDC);

#ifdef _AUTOSCROLLING

	pWnd->ClientToScreen(&oldPoint);
	CRect rectWnd;

	pWnd->GetClientRect(&rectWnd);

	rectWnd.right -= m_rectGroup.Width();
	rectWnd.bottom -= m_rectGroup.Height();
#ifndef _VIWCONTROL
	CScrollView* pView = DYNAMIC_DOWNCAST(CScrollView, pWnd);
#else
	CViwControl* pView = DYNAMIC_DOWNCAST(CViwControl, pWnd);
#endif	
	CPoint point0;
	CPoint point1;
	CPoint ptViewOrg0 = CPoint(0, 0);
	if (pView != NULL)
		ptViewOrg0 = -pView->GetDeviceScrollPosition();
	CPoint ptViewOrg = ptViewOrg0;
#endif

	CRect rectOld;
	BOOL bMoved = FALSE;
	BOOL bScrolled = FALSE;

	int index;
	CRect* pRect;
	m_bFirstErase = TRUE;

	// get messages until capture lost or cancelled/accepted
	for (; ;)
	{
		MSG msg;
		VERIFY(::GetMessage(&msg, NULL, 0, 0));

		if (CWnd::GetCapture() != pWnd)
			break;

		switch (msg.message)
		{
			// handle movement/accept messages
		case WM_LBUTTONUP:
		case WM_MOUSEMOVE:
			// only redraw and callback if the rect actually changed!
			m_bFinalErase = (msg.message == WM_LBUTTONUP);

#ifdef _AUTOSCROLLING
			if (pView != NULL)
			{
				point0.x = (int) (short) LOWORD(msg.lParam);
				point0.y = (int) (short) HIWORD(msg.lParam);

				pWnd->ClientToScreen(&point0);
				point1 = point0;

				point0.x = point0.x - oldPoint.x + m_rectGroup.left;
				point0.y = point0.y - oldPoint.y + m_rectGroup.top;

				rectOld = m_rectGroup;

				int nMode;
				CSize szTotal, szPage, szLine;
				pView->GetDeviceScrollSizes(nMode, szTotal, szPage, szLine);
				bScrolled = FALSE;

				// Erase it before scroll
				CRect rectDummy(0, 0, 0, 0);
				for (index = 0; index < m_CopyPosition.GetSize(); index++)
				{
					rectDummy.CopyRect(m_CopyPosition.GetAt(index));
					EraseTrackerRect(&rectDummy, pWndClipTo, pDrawDC, pWnd);
				}

				if (point0.x < 0)
				{
					pView->SendMessage(WM_HSCROLL, SB_LINEUP);

					ptViewOrg = -pView->GetDeviceScrollPosition();
					pDC->SetViewportOrg(ptViewOrg);

					point1.x -= point0.x;
					point0.x = 0;

					bScrolled = TRUE;
				}
				else if (point0.x > rectWnd.Width())
				{
					pView->SendMessage(WM_HSCROLL, SB_LINEDOWN);
					ptViewOrg = -pView->GetDeviceScrollPosition();
					pDC->SetViewportOrg(ptViewOrg);

					point1.x -= (point0.x - rectWnd.Width());
					point0.x = rectWnd.Width();

					bScrolled = TRUE;					
				}

				if (point0.y <= 0)
				{
					pView->SendMessage(WM_VSCROLL, SB_LINEUP);

					ptViewOrg = -pView->GetDeviceScrollPosition();
					pDC->SetViewportOrg(ptViewOrg);

					point1.y -= point0.y;
					point0.y = 0;

					bScrolled = TRUE;
				}
				else if (point0.y >= rectWnd.Height())
				{
					pView->SendMessage(WM_VSCROLL, SB_LINEDOWN);
					ptViewOrg = -pView->GetDeviceScrollPosition();
					pDC->SetViewportOrg(ptViewOrg);

					point1.y -= (point0.y - rectWnd.Height());
					point0.y = rectWnd.Height();

					bScrolled = TRUE;
				}

				int xDelta = point0.x - m_rectGroup.left;
				int yDelta = point0.y - m_rectGroup.top;
				m_rectGroup.left = point0.x;
				m_rectGroup.right = m_rectGroup.left + rectOld.Width();
				m_rectGroup.top = point0.y;
				m_rectGroup.bottom = m_rectGroup.top + rectOld.Height();

				for (index = 0; index < m_CopyPosition.GetSize(); index++)
				{
					pRect = m_CopyPosition.GetAt(index);

					rectOld.CopyRect(pRect);

					m_bErase = TRUE;
					CRect rectDraw = rectOld;
					if (bScrolled)
					{
						rectDraw.top -= (ptViewOrg0.y - ptViewOrg.y);
						rectDraw.bottom -= (ptViewOrg0.y - ptViewOrg.y);
					}

					// DrawTrackerRect(&rectDraw, pWndClipTo, pDrawDC, pWnd);

					m_rect = rectOld;
					m_rect.left += xDelta;
					m_rect.top += yDelta;

					m_rect.right = m_rect.left + rectOld.Width();
					m_rect.bottom = m_rect.top + rectOld.Height();

					if (!m_bFinalErase)
					{
						bMoved = TRUE;
						m_bErase = FALSE;
						m_rectLast.SetRectEmpty();
						m_sizeLast.cx = 0;
						m_sizeLast.cy = 0;

						DrawTrackerRect(&m_rect, pWndClipTo, pDrawDC, pWnd);
					}

					pRect->CopyRect(m_rect);
				}

				if (bScrolled)
					ptViewOrg0 = ptViewOrg;

				oldPoint = point1;
				if (m_bFinalErase)
					goto ExitLoop;
				break;
			}
			else
			#endif
			{
				for (index = 0; index < m_CopyPosition.GetSize(); index++)
				{
					pRect = m_CopyPosition.GetAt(index);
					m_rect.CopyRect(pRect);

					rectOld = m_rect;

					m_rect.left += (int) (short) LOWORD(msg.lParam) -
						oldPoint.x;
					m_rect.top += (int) (short) HIWORD(msg.lParam) -
						oldPoint.y;

					m_rect.right = m_rect.left + rectOld.Width();
					m_rect.bottom = m_rect.top + rectOld.Height();

					if (!rectOld.EqualRect(&m_rect) || m_bFinalErase)
					{
						if (bMoved)
						{
							m_bErase = TRUE;
							DrawTrackerRect(&rectOld, pWndClipTo, pDrawDC,
								pWnd);
						}
						if (!m_bFinalErase)
							bMoved = TRUE;
					}
					if (!rectOld.EqualRect(&m_rect) && !m_bFinalErase)
					{
						m_bErase = FALSE;
						DrawTrackerRect(&m_rect, pWndClipTo, pDrawDC, pWnd);
					}

					pRect->CopyRect(m_rect);
				}
				oldPoint = msg.lParam;
				if (m_bFinalErase)
					goto ExitLoop;
				break;
			}
			// handle cancel messages
		case WM_KEYDOWN:
			if (msg.wParam != VK_ESCAPE)
				break;
		case WM_RBUTTONDOWN:
			if (bMoved)
			{
				for (int index = 0; index < m_CopyPosition.GetSize(); index++)
				{
					m_rect.CopyRect(m_CopyPosition.GetAt(index));
					m_bErase = m_bFinalErase = TRUE;
					DrawTrackerRect(&m_rect, pWndClipTo, pDrawDC, pWnd);
				}
			}
			// RestoreRectangles (m_rects);
			goto ExitLoop;

			// just dispatch rest of the messages
		default:
			DispatchMessage(&msg);
			break;
		}
	}

	ExitLoop:
	if (pWndClipTo != NULL)
		pWndClipTo->ReleaseDC(pDrawDC);
	else
		pWnd->ReleaseDC(pDrawDC);
	ReleaseCapture();

	AfxUnlockTempMaps(FALSE);

	// update object's rect pos in case bMoved is TRUE
	if (bMoved)
		UpdateObjects(pDC);

	m_bFinalErase = FALSE;
	m_bErase = FALSE;

	// return TRUE only if rect has changed
	return bMoved;
}

CRect CMultiRectTracker::GetGroupRect()
{
	CRect rectGroup(0, 0, 0, 0);
	CRect rect;
	for (int index = 0; index < m_Objects.GetSize(); index++)
	{
		rect = m_Objects.GetAt(index)->GetRect();
		rect.NormalizeRect();
		if (index)
		{
			if (rect.left < rectGroup.left)
				rectGroup.left = rect.left;
			if (rect.right > rectGroup.right)
				rectGroup.right = rect.right;
			if (rect.top < rectGroup.top)
				rectGroup.top = rect.top;
			if (rect.bottom > rectGroup.bottom)
				rectGroup.bottom = rect.bottom;
		}
		else
			rectGroup = rect;
	}

	rect.top = rectGroup.top;
	rectGroup.top = rectGroup.bottom;
	rectGroup.bottom = rect.top;
	
	rectGroup.left -= 10;
	rectGroup.right += 10;
	rectGroup.top += 10;
	rectGroup.bottom -= 10;

	return rectGroup;
}

CRect CMultiRectTracker::GetTotalRect()
{
	CRect rectGroup(0, 0, 0, 0);
	CRect rect;
	POSITION pos = m_lstObjs.GetHeadPosition();
	for (int index = 0; pos != NULL; )
	{
		CMRTObject* pObj = m_lstObjs.GetNext(pos);
		if (NULL == pObj)
			continue;

		rect = pObj->GetRect();
		rect.NormalizeRect();
		if (index)
		{
			if (rect.left < rectGroup.left)
				rectGroup.left = rect.left;
			if (rect.right > rectGroup.right)
				rectGroup.right = rect.right;
			if (rect.top < rectGroup.top)
				rectGroup.top = rect.top;
			if (rect.bottom > rectGroup.bottom)
				rectGroup.bottom = rect.bottom;
		}
		else
			rectGroup = rect;
		index++;
	}

	rect.top = rectGroup.top;
	rectGroup.top = rectGroup.bottom;
	rectGroup.bottom = rect.top;
	
// 	rectGroup.left -= 10;
// 	rectGroup.right += 10;
// 	rectGroup.top += 10;
// 	rectGroup.bottom -= 10;

	return rectGroup;
}

//////////////////////////////////////////////////////////////////////
// Saved rectangle management
//////////////////////////////////////////////////////////////////////
//
//
void CMultiRectTracker::CopyPositions(CDC* pDC)
{
	ClearPositions();

	for (int index = 0; index < m_Objects.GetSize(); index++)
	{
		CRect rect = m_Objects.GetAt(index)->GetRect();
		pDC->LPtoDP(&rect);
		m_CopyPosition.Add(new CRect(rect));
		rect.NormalizeRect();
#ifdef _AUTOSCROLLING
		if (index)
		{
			if (rect.left < m_rectGroup.left)
				m_rectGroup.left = rect.left;
			if (rect.right > m_rectGroup.right)
				m_rectGroup.right = rect.right;
			if (rect.top < m_rectGroup.top)
				m_rectGroup.top = rect.top;
			if (rect.bottom > m_rectGroup.bottom)
				m_rectGroup.bottom = rect.bottom;
		}
		else
		{
			m_rectGroup = rect;
		}
#endif	

	}
}

//////////////////////////////////////////////////////////////////////
//
//
void CMultiRectTracker::UpdateObjects(CDC* pDC)
{
	ASSERT(m_Objects.GetSize() == m_CopyPosition.GetSize());
	for (int index = 0; index < m_Objects.GetSize(); index++)
	{
		CRect* pRect = m_CopyPosition.GetAt(index);
		pDC->DPtoLP(pRect);
		//m_Objects.GetAt(index)->SetRect(pRect);
	}

	AddOprt(new CMRTMove(&m_Objects, &m_CopyPosition));
	ClearPositions();
}

//////////////////////////////////////////////////////////////////////
//
//
void CMultiRectTracker::ClearPositions()
{
	for (int index = 0; index < m_CopyPosition.GetSize(); index++)
		delete m_CopyPosition.GetAt(index);
	m_CopyPosition.RemoveAll();
}

BOOL CMultiRectTracker::PreTranslateMessage(MSG* pMsg, CWnd* pWnd)
{
	if (pMsg->message == WM_KEYDOWN)
	{
		if ((m_pObject != NULL) && (m_pObject->GetObjType() == _T("CellObj")))
			return FALSE;

		CList<CMRTObject*,CMRTObject*>* pObjects = &m_lstObjs;
		if (pMsg->wParam == VK_RETURN)
		{
			if (!isCtrlPressed)
			{
				if (m_pObject != NULL)
					DestroyEdit();
				return TRUE;
			}
		}
		else if (pMsg->wParam == VK_TAB)
		{
			m_Objects.RemoveAll();
			if (m_pObject != NULL)
			{
				DestroyEdit();
				POSITION pos = pObjects->Find(m_pObject);
				if (pos != NULL)
				{
					pObjects->GetNext(pos);
					if (pos != NULL)
						Add(pObjects->GetNext(pos));
					else
						Add(pObjects->GetHead());
				}
				else if (pObjects->GetCount())
					Add(pObjects->GetHead());
			}
			else if (pObjects->GetCount())
			{
				Add(pObjects->GetHead());
			}
			pWnd->Invalidate();
			return TRUE;
		}
		else if (m_pEdit == NULL)
		{
			if (pMsg->wParam == VK_DELETE)
			{
				OnEditDelete();
				pWnd->Invalidate();
				return TRUE;
			}
			else if ((pMsg->wParam >= VK_LEFT) && (pMsg->wParam <= VK_DOWN))
			{
				CMRTMove* pMove = new CMRTMove;
				CArray<CMRTObject*, CMRTObject*>& moveObjs = pMove->m_pOprtObjs;
				CArray<CRect*, CRect*>& moveRects = pMove->m_pRects;
				
				int i = m_Objects.GetSize();
				if (i > 0)
				{
					switch (pMsg->wParam)
					{
					case VK_LEFT:
						for (i--; i > -1; i--)
						{
							CMRTObject* pObj = m_Objects.GetAt(i);
							if (pObj->IsLocked())
								continue;

							CRect* pRect = new CRect(pObj->GetRect());
							pRect->left--;
							pRect->right--;
							moveObjs.Add(pObj);
							moveRects.Add(pRect);
						}
						break;
					case VK_DOWN:
						for (i--; i > -1; i--)
						{
							CMRTObject* pObj = m_Objects.GetAt(i);
							if (pObj->IsLocked())
								continue;

							CRect* pRect = new CRect(pObj->GetRect());
							pRect->top--;
							pRect->bottom--;
							moveObjs.Add(pObj);
							moveRects.Add(pRect);
						}
						break;
					case VK_RIGHT:
						for (i--; i > -1; i--)
						{
							CMRTObject* pObj = m_Objects.GetAt(i);
							if (pObj->IsLocked())
								continue;

							CRect* pRect = new CRect(pObj->GetRect());
							pRect->left++;
							pRect->right++;
							moveObjs.Add(pObj);
							moveRects.Add(pRect);
						}
						break;
					case VK_UP:
						for (i--; i > -1; i--)
						{
							CMRTObject* pObj = m_Objects.GetAt(i);
							if (pObj->IsLocked())
								continue;

							CRect* pRect = new CRect(pObj->GetRect());
							pRect->top++;
							pRect->bottom++;
							moveObjs.Add(pObj);
							moveRects.Add(pRect);
						}
						break;
					}
					AddOprt(pMove);
					pWnd->Invalidate();
					return TRUE;
				}
			}
		}
	}

	return FALSE;
}

void CMultiRectTracker::Select(CMRTOprt* pOprt)
{
	m_Objects.RemoveAll();
	m_pObject = NULL;

	m_Objects.Copy(pOprt->m_pOprtObjs);
	if (m_Objects.GetSize())
	{
		m_pObject = m_Objects[0];
		if (!m_lstObjs.Find(m_pObject))
		{
			m_Objects.RemoveAll();
			m_pObject = NULL;
		}
	}
}

void CMultiRectTracker::Undo(CWnd* pWnd)
{
	if (m_posCurr == NULL)
		return;

	DestroyEdit();
	m_Objects.RemoveAll();
	m_pObject = NULL;
	CMRTOprt* pOprt = m_lstStack.GetPrev(m_posCurr);
	pOprt->Undo();
	if (m_posCurr != NULL)
	{
		pOprt = m_lstStack.GetAt(m_posCurr);
		Select(pOprt);
	}
	pWnd->Invalidate();
}

void CMultiRectTracker::Redo(CWnd* pWnd)
{
	if (m_posCurr == m_lstStack.GetTailPosition())
		return;
	
	if (m_posCurr == NULL)
		m_posCurr = m_lstStack.GetHeadPosition();
	else
		m_lstStack.GetNext(m_posCurr);

	CMRTOprt* pOprt = m_lstStack.GetAt(m_posCurr);
	pOprt->Do();
	Select(pOprt);

	pWnd->Invalidate();
}

void CMultiRectTracker::OnEditProp(CWnd* pWnd)
{
#ifdef _VIWCONTROL

	CMRTProp* pProp = new CMRTProp(m_pObject, m_pObject->GetEditProp());
	CXMLSettings& xml = pProp->m_xml;
	CString strObjClass = m_pObject->GetRuntimeClass()->m_lpszClassName;
	if (m_pObject->EditProp() != IDOK)
	{
		delete pProp;
		return;
	}
	
	if (m_Objects.GetSize() > 1)
	{
		if (AfxMessageBox(IDS_APPLYTO_ALL, MB_YESNO) == IDYES)
		{
			for (int i = m_Objects.GetSize() - 1; i > -1; i--)
			{
				CMRTObject* pObject = m_Objects.GetAt(i);
				if (pObject == m_pObject)
					continue;

				pProp->m_pOprtObjs.Add(pObject);
			}
		}
	}
	AddOprt(pProp);

#endif
	pWnd->Invalidate();
}

void CMultiRectTracker::OnEditCopy(CWnd* pWnd)
{
	if (m_pObject != NULL)
	{
		m_pWnd = pWnd;
		COleDataSource* pSource = new COleDataSource();
		Obj2Clipboard(pSource);
		if (m_pObject->m_strText.GetLength())
		{
			Rtf2Clipboard(pSource);
			Txt2Clipboard(pSource, &m_lstObjs);
		}
		//Wmf2Clipboard(pSource, pObjects);
		Bmp2Clipboard(pSource, &m_lstObjs);
		pSource->SetClipboard();
		m_pWnd = NULL;
	}
}

void CMultiRectTracker::ObjLock()
{
	BOOL bLocked = !m_pObject->IsLocked();
	CMRTProp* pProp = new CMRTProp(m_pObject, 0x0008);
	for (int i = m_Objects.GetSize() - 1; i > -1; i--)
	{
		CMRTObject* pObj = m_Objects.GetAt(i);
		if (pObj != m_pObject)
			pProp->m_pOprtObjs.Add(pObj);
	}
	m_pObject->Lock(bLocked);
	AddOprt(pProp);
}

void CMultiRectTracker::BackLayer()
{
	int nLayer = m_pObject->m_nLayer ? 0 : 1;
	CMRTProp* pProp = new CMRTProp(m_pObject, 0x0001);
	for (int i = m_Objects.GetSize() - 1; i > -1; i--)
	{
		CMRTObject* pObj = m_Objects.GetAt(i);
		if (pObj != m_pObject)
			pProp->m_pOprtObjs.Add(pObj);
	}
	m_pObject->m_nLayer = nLayer;
	AddOprt(pProp);
}

void CMultiRectTracker::OnEditDelete()
{
	CArray<CMRTObject*, CMRTObject*> delObjs;
	for (int i = m_Objects.GetSize() - 1; i > -1; i--)
	{
		CMRTObject* pObj = m_Objects.GetAt(i);
		if (pObj != NULL)
		{
			if (pObj->IsLocked())
				continue;

			delObjs.Add(pObj);
		}
	}
	
	AddOprt(new CMRTDel(&delObjs, &m_lstObjs, &m_lstDels));
	RemoveAll();

	int nSize = m_Objects.GetSize();
	if (nSize > 0)
		m_pObject = m_Objects.GetAt(nSize - 1);
}

void CMultiRectTracker::OnEditCut(CWnd* pWnd)
{
	OnEditCopy(pWnd);
	OnEditDelete();
	pWnd->Invalidate();
}

#define csv_w	200
#define csv_h	 60

void CMultiRectTracker::OnEditPaste(CWnd* pWnd)
{
	COleDataObject obj;
	if (!obj.AttachClipboard())
		return;

	m_pWnd = pWnd;
	FORMATETC fmt;
	obj.BeginEnumFormats();
	while (obj.GetNextFormat(&fmt))
	{
		TCHAR szFormat[_MAX_PATH];
		ZeroMemory(szFormat, _MAX_PATH * sizeof(TCHAR));
		GetClipboardFormatName(fmt.cfFormat, szFormat, _MAX_PATH);
		TRACE(_T("%d %s\n"), fmt.cfFormat, szFormat);
	}

	CPoint point;
	::GetCursorPos(&point);
	pWnd->ScreenToClient(&point);

	CClientDC dc(pWnd);
	OnPrepareDC(&dc);
	dc.DPtoLP(&point);

	if (obj.IsDataAvailable(m_nRCF))
	{
		HGLOBAL hmem = obj.GetGlobalData(m_nRCF);
		CMemFile sf((BYTE*) ::GlobalLock(hmem), ::GlobalSize(hmem));
		CXMLSettings xml;
		if (xml.ReadXMLFromFile(&sf))
		{
			RemoveAll();

			CPoint point0;
			int iCount = 0;
			xml.Read(_T("Point"), point0);
			xml.Read(_T("MRTObjects"), iCount);
			CPoint pointOffset;
			pointOffset.x = point.x - point0.x;
			pointOffset.y = point.y - point0.y;
			for (UINT i = 0; i < iCount; i++)
			{
				if (xml.Open(_T("MRTObject%d"), i))
				{
					CMRTObject* pObject = CMRTObject::CreateObj(xml);

					CRect rect = pObject->GetRect();
					int w = rect.Width();
					int h = rect.Height();
					rect.left += pointOffset.x;
					rect.top += pointOffset.y;
					rect.right = rect.left + w;
					rect.bottom = rect.top + h;
					pObject->SetRect(rect);
					Add(pObject);
					xml.Back();
				}
			}
			AddOprt(new CMRTAdd(&m_Objects, &m_lstObjs, &m_lstAdds));
			m_pWnd->Invalidate();
		}
	}
/*	else if (obj.IsDataAvailable(m_nXMLSF))
	{
		HGLOBAL hmem = obj.GetGlobalData(m_nXMLSF);
		DWORD nSize = ::GlobalSize(hmem);
		CMemFile sf((BYTE*) ::GlobalLock(hmem), nSize);

		CString buffer;

		LPTSTR str = buffer.GetBufferSetLength(nSize);
		sf.Read(str, ::GlobalSize(hmem));
		::GlobalUnlock(hmem);

		char c = buffer[buffer.GetLength()-1];
		if (c == 0)
		{
			buffer = buffer.Left(nSize-1);
		}
		
		CStdioFile file("C:\\1.xml", CStdioFile::modeCreate|CStdioFile::modeWrite);
		file.WriteString(buffer);
	}
	else if (obj.IsDataAvailable(m_nHTMLF))
	{
		HGLOBAL hmem = obj.GetGlobalData(m_nHTMLF);
		DWORD nSize = ::GlobalSize(hmem);
		CMemFile sf((BYTE*) ::GlobalLock(hmem), nSize);

		CString buffer;

		LPTSTR str = buffer.GetBufferSetLength(nSize);
		sf.Read(str, ::GlobalSize(hmem));
		::GlobalUnlock(hmem);

		char c = buffer[buffer.GetLength()-1];
		if (c == 0)
		{
			buffer = buffer.Left(nSize-1);
		}
		
		CStdioFile file("C:\\1.html", CStdioFile::modeCreate|CStdioFile::modeWrite);
		file.WriteString(buffer);
	}
*/	else if (obj.IsDataAvailable(m_nCSVF))
	{
		HGLOBAL hmem = obj.GetGlobalData(m_nCSVF);
		DWORD nSize = ::GlobalSize(hmem);
		CMemFile sf((BYTE*) ::GlobalLock(hmem), nSize);

		CString buffer;

		LPTSTR str = buffer.GetBufferSetLength(nSize);
		sf.Read(str, ::GlobalSize(hmem));
		::GlobalUnlock(hmem);

		char c = buffer[buffer.GetLength()-1];
		if (c == 0)
		{
			buffer = buffer.Left(nSize-1);
		}
		buffer.TrimRight(_T("\n"));

		CRect rect(point.x - 100, point.y + 100, point.x - 100 + csv_w,
			point.y + 100 - csv_h);

		CStringArray strRows;
		SplitString(buffer, '\n', strRows);
		m_Objects.RemoveAll();
		for (int i = 0; i < strRows.GetSize(); i++)
		{
			CStringArray strCols;
			SplitString(strRows[i], ',', strCols);
			for (int j = 0; j < strCols.GetSize(); j++)
			{
				CMRTObject* pObject = new CRectObject;
				pObject->SetRect(rect);
				pObject->m_strText = strCols[j];
				pObject->SetLineWidth(1);
				Add(pObject);
				rect.left = rect.right;
				rect.right = rect.left + csv_w;
			}
			rect.left = point.x - 100;
			rect.right = rect.left + csv_w;
			rect.top = rect.bottom;
			rect.bottom = rect.top - csv_h;
		}
		AddOprt(new CMRTAdd(&m_Objects, &m_lstObjs, &m_lstAdds));
		m_pWnd->Invalidate();
	}
	else if (obj.IsDataAvailable(CF_TEXT))
	{
		HGLOBAL hmem = obj.GetGlobalData(CF_TEXT);

		DWORD nSize = ::GlobalSize(hmem);
		CMemFile sf((BYTE*) ::GlobalLock(hmem), nSize);
		CString buffer;

		LPTSTR str = buffer.GetBufferSetLength(nSize);
		sf.Read(str, ::GlobalSize(hmem));
		::GlobalUnlock(hmem);

		char c = buffer[buffer.GetLength()-1];
		if (c == 0)
		{
			buffer = buffer.Left(nSize-1);
		}

		BOOL bTable = FALSE;
		CStringArray strRows;
		SplitString(buffer, '\n', strRows);
		if (strRows.GetSize() > 1)
		{
			int col = 0;
			for (int i = 0; i < strRows.GetSize(); i++)
			{
				if (strRows.GetAt(i).IsEmpty())
					continue;

				CStringArray strCols;
				SplitString(strRows[i], '\t', strCols);
				if (strCols.GetSize() > 1)
				{
					if (col == 0)
						col = strRows.GetSize();
					else if (col != strRows.GetSize())
					{
						col = 0;
						break;
					}
				}
				else
				{
					col = 0;
					break;
				}
			}

			if (col > 1) // Is a table
			{
				CRect rect(point.x - 100, point.y + 100, point.x - 100 + csv_w, point.y + 100 - csv_h);
				m_Objects.RemoveAll();
				for (int i = 0; i < strRows.GetSize(); i++)
				{	
					if (strRows.GetAt(i).IsEmpty())
						continue;
					
					CStringArray strCols;
					SplitString(strRows[i], '\t', strCols);
					for (int j = 0; j < strCols.GetSize(); j++)
					{
						CMRTObject* pObject = new CRectObject;
						pObject->SetRect(rect);
						pObject->m_strText = strCols[j];
						pObject->SetLineWidth(1);
						Add(pObject);
						rect.left = rect.right;
						rect.right = rect.left + csv_w;
					}
					rect.left = point.x - 100;
					rect.right = rect.left + csv_w;
					rect.top = rect.bottom;
					rect.bottom = rect.top - csv_h;
				}
				AddOprt(new CMRTAdd(&m_Objects, &m_lstObjs, &m_lstAdds));
				m_pWnd->Invalidate();
				return;
			}
		}
		
		if (m_pObject != NULL)
		{
			CMRTProp* pProp = new CMRTProp(m_pObject, 0x0002);
			m_pObject->SetText(buffer);
			AddOprt(pProp);
		}
		else
		{
			CRect rect(point.x - 100, point.y + 100, point.x + 100,
				point.y - 100);
			CMRTObject* pObject = new CRectObject;
			pObject->SetRect(rect);
			pObject->m_strText = buffer;
			AddOprt(new CMRTAdd(pObject, &m_lstObjs, &m_lstAdds));
			Add(pObject);
		}
		m_pWnd->Invalidate();
	}
	else if (obj.IsDataAvailable(CF_BITMAP))
	{
		tagSTGMEDIUM dataMedium;
		if (!obj.GetData(CF_BITMAP, &dataMedium))
			return;

		if (m_pObject != NULL)
		{
			CMRTProp* pProp = new CMRTProp(m_pObject, 0x0040);
			m_pObject->CreatePicture(GetNewPicName(), dataMedium.hBitmap);
			AddOprt(pProp);
		}
		else
		{
			CRect rect(point.x - 100, point.y + 100, point.x + 100,
				point.y - 100);
			CMRTObject* pObject = new CRectObject;
			pObject->SetRect(rect);
			pObject->CreatePicture(GetNewPicName(), dataMedium.hBitmap);
			AddOprt(new CMRTAdd(pObject, &m_lstObjs, &m_lstAdds));
			Add(pObject);
		}
		m_pWnd->Invalidate();
	}
}

void CMultiRectTracker::OnPrepareDC(CDC* pDC)
{
#ifndef _VIWCONTROL
	CScrollView* pView = DYNAMIC_DOWNCAST(CScrollView, m_pWnd);
#else
	CViwControl* pView = DYNAMIC_DOWNCAST(CViwControl, m_pWnd);
#endif
	if (pView != NULL)
		pView->OnPrepareDC(pDC);
}

void CMultiRectTracker::Obj2Clipboard(COleDataSource* pSource)
{
	CSharedFile sf(GMEM_MOVEABLE | GMEM_DDESHARE | GMEM_ZEROINIT);

	CXMLSettings xml(FALSE, _T("MRT"));

	int iCount = m_Objects.GetSize();

	CPoint point;
	::GetCursorPos(&point);
	m_pWnd->ScreenToClient(&point);

	CClientDC dc(m_pWnd);
	OnPrepareDC(&dc);

	dc.DPtoLP(&point);

	xml.Write(_T("Point"), point);
	xml.Write(_T("MRTObjects"), iCount);
	for (UINT i = 0; i < iCount; i++)
	{
		CMRTObject* pObj = m_Objects.GetAt(i);
		if (xml.CreateKey(_T("MRTObject%d"), i))
		{
			pObj->Serialize(xml);
			xml.Back();
		}
	}
	xml.WriteXMLToFile(&sf);

	HGLOBAL hMem = sf.Detach();
	if (!hMem)
		return;
	pSource->CacheGlobalData(m_nRCF, hMem);
}

void CMultiRectTracker::Rtf2Clipboard(COleDataSource* pSource)
{
	CFont* pFont = m_pObject->GetFont();
	if (pFont != NULL)
	{
		UINT nRCF = ::RegisterClipboardFormat(CF_RTF);
		CSharedFile sf(GMEM_MOVEABLE | GMEM_DDESHARE | GMEM_ZEROINIT);

		LOGFONT lf;
		pFont->GetLogFont(&lf);
		CString strText = _T("{\\rtf1 {");
		if (lf.lfWeight >= FW_BOLD)
			strText += _T("\\b");
		if (lf.lfItalic)
			strText += _T("\\i");

		if (strText.Right(1) != _T("{"))
			strText += _T(" ");

		strText += m_pObject->m_strText;
		strText += _T("\\par}}");
		sf.Write(strText, strText.GetLength());

		HGLOBAL hMem = sf.Detach();
		if (!hMem)
			return;
		pSource->CacheGlobalData(nRCF, hMem);
	}
}

void CMultiRectTracker::AddSort(CMRTObject* pObject, CList<CMRTObject*, CMRTObject*>* pList)
{
	if (NULL == pObject)
		return;

	CPoint ptTopLeft = pObject->TopLeft();

	// Sort from top to bottom, left to right
	POSITION pos = pList->GetHeadPosition();
	for (; pos != NULL;)
	{
		POSITION pos0 = pos;
		CMRTObject* pObj = pList->GetNext(pos);

		while (ptTopLeft.y == pObj->TopLeft().y)
		{
			if (ptTopLeft.x < pObj->TopLeft().x)
			{
				pList->InsertBefore(pos0, pObject);
				return;
			}

			if (pos == NULL)
			{
				pList->AddTail(pObject);
				return;
			}

			pos0 = pos;
			pObj = pList->GetNext(pos);
		}

		if (ptTopLeft.y > pObj->TopLeft().y)
		{
			pList->InsertBefore(pos0, pObject);
			return;
		}
	}

	pList->AddTail(pObject);
}

void CMultiRectTracker::Txt2Clipboard(COleDataSource* pSource, CList<CMRTObject*,CMRTObject*>* pObjects)
{
	CSharedFile sf(GMEM_MOVEABLE | GMEM_DDESHARE | GMEM_ZEROINIT);

	CList<CMRTObject*,CMRTObject*> lstObjs;
	POSITION pos = pObjects->GetHeadPosition();
	CMRTObject* pObj = pObjects->GetNext(pos);
	lstObjs.AddTail(pObj);
	for ( ; pos != NULL; )
	{
		AddSort(pObjects->GetNext(pos), &lstObjs);
	}

	CString strText = m_pObject->m_strText;
	pos = lstObjs.GetHeadPosition();
	if (lstObjs.GetCount() > 1)
	{
		pObj = lstObjs.GetNext(pos);
		if (Find(pObj) != -1)
			strText = pObj->m_strText;
		else
			strText = _T("");
		
		CPoint ptTopLeft = pObj->TopLeft();
		for ( ; pos != NULL; )
		{
			pObj = lstObjs.GetNext(pos);
			if (pObj->TopLeft().y != ptTopLeft.y)
			{
				strText += _T("\r\n");
				if (Find(pObj) != -1)
					strText += pObj->m_strText;
				ptTopLeft.y = pObj->TopLeft().y;
				continue;
			}

			strText += _T("\t");
			if (Find(pObj) != -1)
				strText += pObj->m_strText;
		}
	}

	sf.Write(strText, strText.GetLength());

	HGLOBAL hMem = sf.Detach();
	if (!hMem)
		return;
	pSource->CacheGlobalData(CF_TEXT, hMem);
}

void CMultiRectTracker::Wmf2Clipboard(COleDataSource* pSource, CList<CMRTObject*,CMRTObject*>* pObjects)
{
	CMetaFileDC* pMetaDC = new CMetaFileDC();

	CRect rect = GetGroupRect();

	CClientDC dc(m_pWnd);
	OnPrepareDC(&dc);

	if (pMetaDC->CreateEnhanced(&dc, NULL, rect, _T("whatever")))
	{
		//draw meta file
		pMetaDC->FillSolidRect(&rect, RGB(255, 255, 255));
		POSITION pos = pObjects->GetTailPosition();
		while (pos != NULL)
		{
			CMRTObject* pObj = pObjects->GetPrev(pos);
			if (Find(pObj) != -1)
				pObj->Draw(pMetaDC);
		}
		
		//do what ever you want to do: bitmaps, lines, text...
		
		//close meta file dc and prepare for clipboard;
		HENHMETAFILE hMF = pMetaDC->CloseEnhanced();
		pSource->CacheGlobalData(CF_ENHMETAFILE, hMF);
	}
	//DeleteMetaFile(hMF);
	delete pMetaDC;
}

void CMultiRectTracker::Bmp2Clipboard(COleDataSource* pSource,
	CList<CMRTObject*, CMRTObject*>* pObjects)
{
	tagSTGMEDIUM* data = new tagSTGMEDIUM;
	CClientDC dc(m_pWnd);
	OnPrepareDC(&dc);

	CRect rect = GetGroupRect();
	CMyMemDC dcMem(&dc, &rect);
	dcMem.FillSolidRect(&rect, RGB(255, 255, 255));
	POSITION pos = pObjects->GetTailPosition();
	while (pos != NULL)
	{
		CMRTObject* pObj = pObjects->GetPrev(pos);
		if (Find(pObj) != -1)
			pObj->Draw(&dcMem);
	}

	dcMem.SelectObject(&dcMem.m_oldBitmap);
	dcMem.m_pDC = NULL;
	data->tymed = TYMED_GDI;
	data->hBitmap = (HBITMAP) dcMem.m_bitmap.Detach();
	data->pUnkForRelease = NULL;
	pSource->CacheData(CF_BITMAP, data);
	delete data;
}

void CMultiRectTracker::OnUpdateEditPaste(CCmdUI* pCmdUI)
{
	COleDataObject obj;
	if (obj.AttachClipboard())
	{
		pCmdUI->Enable(obj.IsDataAvailable(m_nRCF) ||
					obj.IsDataAvailable(m_nCSVF) ||
					obj.IsDataAvailable(m_nRTFF) ||
					obj.IsDataAvailable(CF_TEXT) ||
					obj.IsDataAvailable(CF_BITMAP));
		obj.Detach();
	}
	else
		pCmdUI->Enable(FALSE);
}

CString CMultiRectTracker::GetNewPicName()
{
	TCHAR szPath[_MAX_PATH];
	GetModuleFileName(NULL, szPath, _MAX_PATH);
	CString m_strIDEPath = szPath;
	m_strIDEPath = m_strIDEPath.Left(m_strIDEPath.ReverseFind('\\'));
	m_strIDEPath = m_strIDEPath.Left(m_strIDEPath.ReverseFind('\\'));

	CString strPic = m_strIDEPath;
	strPic += _T("\\Template\\Pictures\\");
	if (_tchdir(strPic))
		_tmkdir(strPic);

	CTime time = CTime::GetCurrentTime();
	CString strTime = time.Format("%Y%m%d%H%M%S");
	strPic += strTime;
	strPic += _T(".bmp");
	return strPic;
}

BOOL CMultiRectTracker::TrackHandleEx(int nHandle, CWnd* pWnd, CPoint point, CWnd* pWndClipTo, CDC* pDC)
{
	ASSERT(nHandle >= 0);
	ASSERT(nHandle <= 8);   // handle 8 is inside the rect

	// don't handle if capture already set
	if (::GetCapture() != NULL)
		return FALSE;

	AfxLockTempMaps();  // protect maps while looping

	ASSERT(!m_bFinalErase);

	// save original width & height in pixels
	int nWidth = m_rect.Width();
	int nHeight = m_rect.Height();

	CPoint oldPoint = point;

	// set capture to the window which received this message
	pWnd->SetCapture();
	ASSERT(pWnd == CWnd::GetCapture());
	pWnd->UpdateWindow();
	if (pWndClipTo != NULL)
		pWndClipTo->UpdateWindow();
	CRect rectSave = m_rect;

	// find out what x/y coords we are supposed to modify
	int *px, *py;
	int xDiff, yDiff;
	GetModifyPointers(nHandle, &px, &py, &xDiff, &yDiff);
	xDiff = point.x - xDiff;
	yDiff = point.y - yDiff;

	// get DC for drawing
	CDC* pDrawDC;
	if (pWndClipTo != NULL)
	{
		// clip to arbitrary window by using adjusted Window DC
		pDrawDC = pWndClipTo->GetDCEx(NULL, DCX_CACHE);
	}
	else
	{
		// otherwise, just use normal DC
		pDrawDC = pWnd->GetDC();
	}
	ASSERT_VALID(pDrawDC);

#ifdef _AUTOSCROLLING
	pWnd->ClientToScreen(&oldPoint);

	CRect rectWnd;
	pWnd->GetClientRect(&rectWnd);

#ifndef _VIWCONTROL
	CScrollView* pView = DYNAMIC_DOWNCAST(CScrollView, pWnd);
#else
	CViwControl* pView = DYNAMIC_DOWNCAST(CViwControl, pWnd);
#endif
	CPoint ptViewOrg0 = CPoint(0, 0);
	if (pView != NULL)
		ptViewOrg0 = -pView->GetDeviceScrollPosition();
	CPoint ptViewOrg = ptViewOrg0;
#endif

	CRect rectOld;
	BOOL bMoved = FALSE;
	BOOL bScrolled = FALSE;

	// get messages until capture lost or cancelled/accepted
	for (;;)
	{
		MSG msg;
		VERIFY(::GetMessage(&msg, NULL, 0, 0));

		if (CWnd::GetCapture() != pWnd)
			break;

		switch (msg.message)
		{
		// handle movement/accept messages
		case WM_LBUTTONUP:
		case WM_MOUSEMOVE:
			rectOld = m_rect;
			m_bFinalErase = (msg.message == WM_LBUTTONUP);
#ifdef _AUTOSCROLLING
			if (pView != NULL)
			{
				bScrolled = FALSE;

				// Erase it before scroll
				CRect rectDummy = m_rect;
				EraseTrackerRect(&rectDummy, pWndClipTo, pDrawDC, pWnd);
				
				// handle resize cases (and part of move)
				if (px != NULL)
					*px = (int)(short)LOWORD(msg.lParam) - xDiff;
				if (py != NULL)
					*py = (int)(short)HIWORD(msg.lParam) - yDiff;

				// allow caller to adjust the rectangle if necessary
				AdjustRect(nHandle, &m_rect);

				// handle resize cases (and part of move)
				if (px != NULL)
				{
					// *px = (int)(short)LOWORD(msg.lParam) - xDiff;
					if (*px < 0)
					{
						*px = 0;
					}
					else if (*px > rectWnd.Width())
					{
						*px = rectWnd.Width();
					}
				}

				if (py != NULL)
				{
					// *py = (int)(short)HIWORD(msg.lParam) - yDiff;
					if (*py <= 0)
					{
						pView->SendMessage(WM_VSCROLL, SB_LINEUP);
						
						ptViewOrg = -pView->GetDeviceScrollPosition();
						pDC->SetViewportOrg(ptViewOrg);
						*py = 0;
						bScrolled = TRUE;
					}
					else if (*py >= rectWnd.Height())
					{
						pView->SendMessage(WM_VSCROLL, SB_LINEDOWN);
						ptViewOrg = -pView->GetDeviceScrollPosition();
						pDC->SetViewportOrg(ptViewOrg);
						*py = rectWnd.Height();
						bScrolled = TRUE;
					}
				}
				
				if (!m_bFinalErase)
				{
					bMoved = TRUE;
					m_bErase = FALSE;
					m_rectLast.SetRectEmpty();
					m_sizeLast.cx = 0;
					m_sizeLast.cy = 0;
					// TRACE(_T("Draw Rect %d %d %d %d\n"), m_rect.left, m_rect.top, m_rect.right, m_rect.bottom);
					DrawTrackerRect(&m_rect, pWndClipTo, pDrawDC, pWnd);
				}

				if (bScrolled)
					ptViewOrg0 = ptViewOrg;

				if (m_bFinalErase)
					goto ExitLoop;
				break;
			}
			else
			#endif
			{
				
				
				// handle resize cases (and part of move)
				if (px != NULL)
					*px = (int)(short)LOWORD(msg.lParam) - xDiff;
				if (py != NULL)
					*py = (int)(short)HIWORD(msg.lParam) - yDiff;
				
				// handle move case
				if (nHandle == hitMiddle)
				{
					m_rect.right = m_rect.left + nWidth;
					m_rect.bottom = m_rect.top + nHeight;
				}
				// allow caller to adjust the rectangle if necessary
				AdjustRect(nHandle, &m_rect);
				
				// only redraw and callback if the rect actually changed!
				m_bFinalErase = (msg.message == WM_LBUTTONUP);
				if (!rectOld.EqualRect(&m_rect) || m_bFinalErase)
				{
					if (bMoved)
					{
						m_bErase = TRUE;
						DrawTrackerRect(&rectOld, pWndClipTo, pDrawDC, pWnd);
					}
					OnChangedRect(rectOld);
					if (msg.message != WM_LBUTTONUP)
						bMoved = TRUE;
				}
				if (m_bFinalErase)
					goto ExitLoop;
				
				if (!rectOld.EqualRect(&m_rect))
				{
					m_bErase = FALSE;
					DrawTrackerRect(&m_rect, pWndClipTo, pDrawDC, pWnd);
				}
			}
			break;
		// handle cancel messages
		case WM_KEYDOWN:
			if (msg.wParam != VK_ESCAPE)
				break;
		case WM_RBUTTONDOWN:
			if (bMoved)
			{
				m_bErase = m_bFinalErase = TRUE;
				DrawTrackerRect(&m_rect, pWndClipTo, pDrawDC, pWnd);
			}
			m_rect = rectSave;
			goto ExitLoop;

		// just dispatch rest of the messages
		default:
			DispatchMessage(&msg);
			break;
		}
	}

ExitLoop:
	if (pWndClipTo != NULL)
		pWndClipTo->ReleaseDC(pDrawDC);
	else
		pWnd->ReleaseDC(pDrawDC);
	ReleaseCapture();

	AfxUnlockTempMaps(FALSE);

	// restore rect in case bMoved is still FALSE
	if (!bMoved)
		m_rect = rectSave;
	m_bFinalErase = FALSE;
	m_bErase = FALSE;

	// return TRUE only if rect has changed
	return !rectSave.EqualRect(&m_rect);
}

void CMultiRectTracker::Align(DWORD alignment, CMRTObject* pObj0 /* = NULL */)
{
	CArray<CMRTObject*, CMRTObject*>* objects = &m_Objects;
	pObj0 = m_pObject;

	int i = objects->GetSize();
	if (i < 1)
		return;

	int j = 0;
	if (pObj0 == NULL)
	{
		pObj0 = (alignment & (ALIGN_HORZ | ALIGN_VERT)) ?
			objects->GetAt(0) :
			objects->GetAt(i -
						1);
	}

	CMRTMove* pMove = new CMRTMove;
	CArray<CMRTObject*, CMRTObject*>& moveObjs = pMove->m_pOprtObjs;
	CArray<CRect*, CRect*>& moveRects = pMove->m_pRects;

	CRect rect0 = pObj0->GetRect();
	int nAlign;
	switch (alignment)
	{
	case ALIGN_LEFT:
		nAlign = min(rect0.left, rect0.right);
		for (i--; i > -1; i--)
		{
			CMRTObject* pObj = objects->GetAt(i);
			if (pObj->IsLocked() || (pObj == pObj0))
				continue;

			CRect* pRect = new CRect(pObj->GetRect());
			int iWidth = pRect->Width();
			if (iWidth < 0)
			{
				pRect->right = nAlign;
				pRect->left = nAlign - iWidth;
			}
			else
			{
				pRect->left = nAlign;
				pRect->right = nAlign + iWidth;
			}

			moveObjs.Add(pObj);
			moveRects.Add(pRect);
		}
		break;
	case ALIGN_CENTER:
		nAlign = (rect0.left + rect0.right)/2;
		for (i--; i > -1; i--)
		{
			CMRTObject* pObj = objects->GetAt(i);
			if (pObj->IsLocked() || (pObj == pObj0))
				continue;
			
			CRect* pRect = new CRect(pObj->GetRect());
			int iWidth = pRect->Width();
			pRect->left = nAlign - iWidth / 2;
			pRect->right = pRect->left + iWidth;
			moveObjs.Add(pObj);
			moveRects.Add(pRect);
		}
		break;
	case ALIGN_RIGHT:
		nAlign = max(rect0.left, rect0.right);
		for (i--; i > -1; i--)
		{
			CMRTObject* pObj = objects->GetAt(i);
			if (pObj->IsLocked() || (pObj == pObj0))
				continue;

			CRect* pRect = new CRect(pObj->GetRect());
			int iWidth = pRect->Width();
			if (iWidth < 0)
			{
				pRect->left = nAlign;
				pRect->right = nAlign + iWidth;
			}
			else
			{
				pRect->right = nAlign;
				pRect->left = nAlign - iWidth;
			}
			moveObjs.Add(pObj);
			moveRects.Add(pRect);
		}
		break;
	case ALIGN_TOP:
		nAlign = max(rect0.top, rect0.bottom);
		for (i--; i > -1; i--)
		{
			CMRTObject* pObj = objects->GetAt(i);
			if (pObj->IsLocked() || (pObj == pObj0))
				continue;

			CRect* pRect = new CRect(pObj->GetRect());
			int iHeight = pRect->Height();
			if (iHeight < 0)
			{
				pRect->top = nAlign;
				pRect->bottom = nAlign + iHeight;
			}
			else
			{
				pRect->bottom = nAlign;
				pRect->top = nAlign - iHeight;
			}
			moveObjs.Add(pObj);
			moveRects.Add(pRect);
		}
		break;
	case ALIGN_VCENTER:
		nAlign = (rect0.top + rect0.bottom) / 2;
		for (i--; i > -1; i--)
		{
			CMRTObject* pObj = objects->GetAt(i);
			if (pObj->IsLocked() || (pObj == pObj0))
				continue;

			CRect* pRect = new CRect(pObj->GetRect());
			int iHeight = pRect->Height();
			pRect->top = nAlign - iHeight / 2;
			pRect->bottom = pRect->top + iHeight;
			moveObjs.Add(pObj);
			moveRects.Add(pRect);
		}
		break;
	case ALIGN_BOTTOM:
		nAlign = min(rect0.top, rect0.bottom);
		for (i--; i > -1; i--)
		{
			CMRTObject* pObj = objects->GetAt(i);
			if (pObj->IsLocked() || (pObj == pObj0))
				continue;

			CRect* pRect = new CRect(pObj->GetRect());
			int iHeight = pRect->Height();
			if (iHeight < 0)
			{
				pRect->bottom = nAlign;
				pRect->top = nAlign - iHeight;
			}
			else
			{
				pRect->top = nAlign;
				pRect->bottom = nAlign + iHeight;
			}
			moveObjs.Add(pObj);
			moveRects.Add(pRect);
		}
		break;
	case ALIGN_HORZ:
		nAlign = max(rect0.left, rect0.right);
		for (; j < i; j++)
		{
			CMRTObject* pObj = objects->GetAt(j);
			if (pObj->IsLocked() || (pObj == pObj0))
				continue;

			CRect* pRect = new CRect(pObj->GetRect());
			int iWidth = pRect->Width();
			if (iWidth < 0)
			{
				pRect->right = nAlign;
				pRect->left = nAlign - iWidth;
				nAlign = pRect->left;
			}
			else
			{
				pRect->left = nAlign;
				pRect->right = nAlign + iWidth;
				nAlign = pRect->right;
			}
			moveObjs.Add(pObj);
			moveRects.Add(pRect);
		}
		break;
	case ALIGN_VERT:
		nAlign = min(rect0.top, rect0.bottom);
		for (; j < i; j++)
		{
			CMRTObject* pObj = objects->GetAt(j);
			if (pObj->IsLocked() || (pObj == pObj0))
				continue;

			CRect* pRect = new CRect(pObj->GetRect());
			int iHeight = pRect->Height();
			if (iHeight < 0)
			{
				pRect->top = nAlign;
				pRect->bottom = nAlign + iHeight;
				nAlign = pRect->bottom;
			}
			else
			{
				pRect->bottom = nAlign;
				pRect->top = nAlign - iHeight;
				nAlign = pRect->top;
			}
			moveObjs.Add(pObj);
			moveRects.Add(pRect);
		}
		break;
	case ALIGN_RESET:
		{
			CList<CMRTObject*,CMRTObject*> lstObjs;
			int k = 0;
			CMRTObject* pObj = objects->GetAt(0);
			lstObjs.AddTail(pObj);
			for (k = 1; k < objects->GetSize(); k++)
			{
				AddSort(objects->GetAt(k), &lstObjs);
			}
			
			POSITION pos = lstObjs.GetHeadPosition();
			if (lstObjs.GetCount() > 1)
			{
				pObj0 = lstObjs.GetNext(pos);
				
				CPoint ptTopLeft = pObj0->TopLeft();
				CPoint ptBottomRight = pObj0->BottomRight();
				int nAlign = ptBottomRight.x;
				int nBottom = ptBottomRight.y;
				for ( ; pos != NULL; )
				{
					pObj = lstObjs.GetNext(pos);
					
					CRect* pRect = new CRect(pObj->GetRect());
					if (abs(pObj->TopLeft().y - ptTopLeft.y) < 60)
					{
						// Horizontal arrange
						int iWidth = pRect->Width();
						if (iWidth < 0)
						{
							pRect->right = nAlign;
							pRect->left = nAlign - iWidth;
							nAlign = pRect->left;
						}
						else
						{
							pRect->left = nAlign;
							pRect->right = nAlign + iWidth;
							nAlign = pRect->right;
						}

						// Top align
						int iHeight = pRect->Height();
						if (iHeight < 0)
						{
							pRect->top = ptTopLeft.y;
							pRect->bottom = ptTopLeft.y + iHeight;
						}
						else
						{
							pRect->bottom = ptTopLeft.y;
							pRect->top = ptTopLeft.y - iHeight;
						}

						moveObjs.Add(pObj);
						moveRects.Add(pRect);
						continue;
					}  // >= 60
					
					ptTopLeft.y = nBottom;

					// Vertical arrange
					int iHeight = pRect->Height();
					if (iHeight < 0)
					{
						pRect->top = nBottom;
						pRect->bottom = nBottom + iHeight;
						nBottom = pRect->bottom;
					}
					else
					{
						pRect->bottom = nBottom;
						pRect->top = nBottom - iHeight;
						nBottom = pRect->top;
					}

					// Left align
					nAlign = ptTopLeft.x;
					int iWidth = pRect->Width();
					if (iWidth < 0)
					{
						pRect->right = nAlign;
						pRect->left = nAlign - iWidth;
						nAlign = pRect->left;
					}
					else
					{
						pRect->left = nAlign;
						pRect->right = nAlign + iWidth;
						nAlign = pRect->right;
					}
					
					moveObjs.Add(pObj);
					moveRects.Add(pRect);
				}
			}
		}
		break;
	default:
		break;
	}

	if (alignment & SAME_SIZE)
	{
		i = objects->GetSize();
		// pObj0 = objects->GetAt(i);
		CRect rect0 = pObj0->GetRect();
		// rect0.NormalizeRect();
		int iWidth = abs(rect0.Width());
		int iHeight = abs(rect0.Height());
		for (i--; i > -1; i--)
		{
			CMRTObject* pObj = objects->GetAt(i);
			if (pObj->IsLocked() || (pObj == pObj0))
				continue;

			CRect* pRect = new CRect(pObj->GetRect());
			if (alignment & SAME_WIDTH)
			{
				if (pRect->right > pRect->left)
					pRect->right = pRect->left + iWidth;
				else
					pRect->right = pRect->left - iWidth;
			}

			if (alignment & SAME_HEIGHT)
			{
				if (pRect->bottom > pRect->top)
					pRect->bottom = pRect->top + iHeight;
				else
					pRect->bottom = pRect->top - iHeight;
			}
			moveObjs.Add(pObj);
			moveRects.Add(pRect);
		}
	}
	AddOprt(pMove);
}

void CMultiRectTracker::AlignMMText(CArray<CMRTObject*, CMRTObject*>* objects, DWORD alignment, CMRTObject* pObj0 /* = NULL */)
{
	int i = objects->GetSize();
	if (i < 1)
		return;

	int j = 0;
	if (pObj0 == NULL)
	{
		pObj0 = (alignment & (ALIGN_HORZ | ALIGN_VERT)) ?
			objects->GetAt(0) :
			objects->GetAt(i -
						1);
	}

	CMRTMove* pMove = new CMRTMove;
	CArray<CMRTObject*, CMRTObject*>& moveObjs = pMove->m_pOprtObjs;
	CArray<CRect*, CRect*>& moveRects = pMove->m_pRects;

	CRect rect0 = pObj0->GetRect();
	int nAlign;
	switch (alignment)
	{
	case ALIGN_LEFT:
		nAlign = rect0.left;
		for (i--; i > -1; i--)
		{
			CMRTObject* pObj = objects->GetAt(i);
			if (pObj->IsLocked() || (pObj == pObj0))
				continue;

			CRect* pRect = new CRect(pObj->GetRect());
			int iWidth = pRect->Width();
			pRect->left = nAlign;
			pRect->right = pRect->left + iWidth;
			moveObjs.Add(pObj);
			moveRects.Add(pRect);
		}
		break;
	case ALIGN_CENTER:
		nAlign = (rect0.left + rect0.right) / 2;
		for (i--; i > -1; i--)
		{
			CMRTObject* pObj = objects->GetAt(i);
			if (pObj->IsLocked() || (pObj == pObj0))
				continue;

			CRect* pRect = new CRect(pObj->GetRect());
			int iWidth = pRect->Width();
			pRect->left = nAlign - iWidth / 2;
			pRect->right = pRect->left + iWidth;
			moveObjs.Add(pObj);
			moveRects.Add(pRect);
		}
		break;
	case ALIGN_RIGHT:
		nAlign = rect0.right;
		for (i--; i > -1; i--)
		{
			CMRTObject* pObj = objects->GetAt(i);
			if (pObj->IsLocked() || (pObj == pObj0))
				continue;

			CRect* pRect = new CRect(pObj->GetRect());
			int iWidth = pRect->Width();
			pRect->right = nAlign;
			pRect->left = pRect->right - iWidth;
			moveObjs.Add(pObj);
			moveRects.Add(pRect);
		}
		break;
	case ALIGN_TOP:
		nAlign = rect0.top;
		for (i--; i > -1; i--)
		{
			CMRTObject* pObj = objects->GetAt(i);
			if (pObj->IsLocked() || (pObj == pObj0))
				continue;

			CRect* pRect = new CRect(pObj->GetRect());
			int iHeight = pRect->Height();
			pRect->top = nAlign;
			pRect->bottom = pRect->top + iHeight;
			moveObjs.Add(pObj);
			moveRects.Add(pRect);
		}
		break;
	case ALIGN_VCENTER:
		nAlign = (rect0.top + rect0.bottom) / 2;
		for (i--; i > -1; i--)
		{
			CMRTObject* pObj = objects->GetAt(i);
			if (pObj->IsLocked() || (pObj == pObj0))
				continue;

			CRect* pRect = new CRect(pObj->GetRect());
			int iHeight = pRect->Height();
			pRect->top = nAlign - iHeight / 2;
			pRect->bottom = pRect->top + iHeight;
			moveObjs.Add(pObj);
			moveRects.Add(pRect);
		}
		break;
	case ALIGN_BOTTOM:
		nAlign = rect0.bottom;
		for (i--; i > -1; i--)
		{
			CMRTObject* pObj = objects->GetAt(i);
			if (pObj->IsLocked() || (pObj == pObj0))
				continue;

			CRect* pRect = new CRect(pObj->GetRect());
			int iHeight = pRect->Height();
			pRect->bottom = nAlign;
			pRect->top = pRect->bottom - iHeight;
			moveObjs.Add(pObj);
			moveRects.Add(pRect);
		}
		break;
	case ALIGN_HORZ:
		nAlign = rect0.right;
		for (; j < i; j++)
		{
			CMRTObject* pObj = objects->GetAt(j);
			if (pObj->IsLocked() || (pObj == pObj0))
				continue;

			CRect* pRect = new CRect(pObj->GetRect());
			int iWidth = pRect->Width();
			pRect->left = nAlign;
			pRect->right = pRect->left + iWidth;
			nAlign = pRect->right;
			moveObjs.Add(pObj);
			moveRects.Add(pRect);
		}
	case ALIGN_VERT:
		nAlign = rect0.bottom;
		for (; j < i; j++)
		{
			CMRTObject* pObj = objects->GetAt(j);
			if (pObj->IsLocked() || (pObj == pObj0))
				continue;

			CRect* pRect = new CRect(pObj->GetRect());
			int iHeight = pRect->Height();
			pRect->top = nAlign;
			pRect->bottom = pRect->top + iHeight;
			nAlign = pRect->bottom;
			moveObjs.Add(pObj);
			moveRects.Add(pRect);
		}
	default:
		break;
	}

	if (alignment & SAME_SIZE)
	{
		i = objects->GetSize();
		// pObj0 = objects->GetAt(i);
		CRect rect0 = pObj0->GetRect();
		int iWidth = rect0.Width();
		int iHeight = rect0.Height();
		for (i--; i > -1; i--)
		{
			CMRTObject* pObj = objects->GetAt(i);
			if (pObj->IsLocked() || (pObj == pObj0))
				continue;

			CRect* pRect = new CRect(pObj->GetRect());
			if (alignment & SAME_WIDTH)
				pRect->right = pRect->left +
					iWidth;
			if (alignment & SAME_HEIGHT)
				pRect->bottom = pRect->top +
					iHeight;
			moveObjs.Add(pObj);
			moveRects.Add(pRect);
		}
	}
	pMove->Do();
	delete pMove;
}

void CMultiRectTracker::SetDoc(IULFile* pDoc)
{
	m_pDoc = pDoc;

	POSITION pos = m_lstObjs.GetHeadPosition();
	while (pos != NULL)
		m_lstObjs.GetNext(pos)->SetDoc(pDoc);

	pos = m_lstAdds.GetHeadPosition();
	while (pos != NULL)
		m_lstAdds.GetNext(pos)->SetDoc(pDoc);

	pos = m_lstDels.GetHeadPosition();
	while (pos != NULL)
		m_lstDels.GetNext(pos)->SetDoc(pDoc);
	
	pos = m_lstGrps.GetHeadPosition();
	while (pos != NULL)
		m_lstGrps.GetNext(pos)->SetDoc(pDoc);
}

